<?php
/**
 *
 * User:
 * Date:
 * Time:
 */

namespace App\Common\RedisCache;

Abstract class AbstractCache implements Cache
{
    /**
     * 当前的key
     * @var string
     */
    protected $key = '';
    /**
     * 当前的过期时间（秒）
     * @var int
     */
    protected $expire = 0;
    /**
     * 原始数据
     * @var string
     */
    protected $raw_data = '';
    /**
     * key前缀
     * @var string
     */
    protected static $prefix = '';
    /**
     * 使用的库
     * @var int
     */
    protected static $db = 0;
    /**
     * 配置名称
     * @var string|null
     */
    protected static $configName = null;
    /**
     * Redis客户端实例
     * @var \Redis|\RedisCluster
     */
    protected static $client;

    protected static $cluster;

    public function __construct()
    {
        if (empty(static::$client)) {
            static::$client = static::getClientInstance();
        }
    }

    public function __destruct()
    {
        static::closeClientInstance();
    }

    public function getDb()
    {
        if (static::$cluster) {
            return 0;
        }
        return $this::$db;
    }

    public function getPrefix()
    {
        return $this::$prefix;
    }


    /**
     * 获取key
     * @return string
     */
    public function getKey()
    {
        return $this->key;
    }

    /**
     * 获取原始数据
     * @return string
     */
    public function getRawData()
    {
        return $this->raw_data;
    }

    /**
     * 设置key生成参数
     * @param array $parameter
     * @return $this
     */
    public function setKeyParameter(array $parameter = [])
    {
        $this->key = $this::key($parameter);
        return $this;
    }

    /**
     * 查询剩余过期时间（秒）
     * @param  string or int $key  键名
     * @return int 返回剩余的时间，如果已过期则返回负数
     */
    public function expiretime($key)
    {
        $this->select();
        return $this::$client->ttl($key);
    }

    /**
     * key的生成方法
     * @param array $parameter
     * @return string
     */
    abstract protected static function key(array $parameter): string ;

    /**
     * 获取所有类似key
     * @param string $key
     * @return array
     */
    public function keys($key = ''): array
    {
        $this->select();
        if (empty($key)) {
            $key = $this->key;
        }
        return $this::$client->keys($key.'*');
    }

    /**
     * 删除key
     * @param string $key
     * @return int
     */
    public function del($key = ''): int
    {
        $this->select();

        if (empty($key)) {
            $key = $this->key;
        }
        return $this::$client->del($key);
    }

    /**
     * 是否存在key
     * @param string $key
     * @return bool
     */
    public function exists($key = ''): bool
    {
        $this->select();

        if (empty($key)) {
            $key = $this->key;
        }
        return $this::$client->exists($key);
    }

    /**
     * 设置过期（秒）
     * ttl等于0时清除过期
     * @param string $key
     * @param int $ttl
     * @return bool
     */
    public function expire($key = '', $ttl = -1): bool
    {
        $this->select();

        if (empty($key)) {
            $key = $this->key;
        }
        if ($ttl == 0) {
            $r = $this::$client->persist($key);
        } elseif ($ttl > 0) {
            $r = $this::$client->expire($key, $ttl);
        } else {
            $r = $this::$client->expire($key, $this->expire);
        }
        return $r;
    }

    /**
     * 获取元素长度或数量
     * @param string $key
     * @return int
     */
    abstract public function len($key = ''): int ;

    /**
     * 切换操作的库
     * @return bool
     */
    protected function select(): bool
    {
        if (static::$cluster) {
            return true;
        }
        return $this::$client->select($this::$db);
    }

    /**
     * 获取Redis客户端实例
     * @throws \Exception
     */
    public static function getClientInstance()
    {
        $config = config('database.redis');
        try{
            if (isset($config['cluster']) && $config['cluster'] == true) {
                $clusters = [];
                $password = $config['options']['password'];
                if (isset($config['clusters'])) {
                    foreach ($config['clusters']['default'] as $v) {
                        $clusters[] = "{$v['host']}:{$v['port']}";
                    }
                }
                $rdb = new \RedisCluster(null, $clusters, 60, null, true, $password);
                $rdb->setOption(\RedisCluster::OPT_READ_TIMEOUT, -1);

                static::$cluster = true;
            } else {
                $host = $config['default']['host'];
                $port = $config['default']['port'];
                $password = $config['default']['password'];
                $rdb = new \Redis();
                $rdb->pconnect($host, $port, 60);
                $rdb->auth($password);
                $rdb->setOption(\Redis::OPT_READ_TIMEOUT, -1);

                static::$cluster = false;
            }
            return $rdb;
        } catch (\Exception $e) {
            \Sentry\captureException($e);
            throw new \Exception($e->getMessage());
        }
    }

    public static function closeClientInstance(): bool
    {
        if (static::$client instanceof \Redis && static::$client->isConnected()) {
            return static::$client->close();
        } else {
            return static::$client->close();
        }
    }
}
